package com.woniu.components.DAO;

import org.hibernate.*;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.metadata.ClassMetadata;

import javax.persistence.EntityManager;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.List;

/**
 * Created by IntelliJ IDEA.
 * User: xujin
 * Date: 2010-2-14
 * Time: 16:57:02
 * 公用DAO辅助类
 */
public class DAOHelper {
    private IEntityManagerHelper entityManagerHelper;
    private EntityManager entityManager;
    private Session session;
    private ClassMetadata classMetadata;
    private boolean isCacheable = true;
    /**
     * 根据条件分页查询记录列表
     *
     * @param entityClass 实体类型
     * @param list        查询条件集合,当参数值为 null 时,查询对象的所有记录数。当参数不会空时,按条件查询指定的对象列表集。
     * @param disPage     分页对象,当参数值为 null 时,所查询的对象列表集为数据库中所有记录。当参数不会空时,按对象所指定的开始记录号与每页记录数查询对象列表集。开始记录号默认值为0,每页记录数默认值为20.
     * @param orders      排序条件集合,当参数值为 null 时,不对查询对象的列表集进行排序。
     * @return 记录列表
     */
    public List<?> findList(Class entityClass, List<Criterion> list, DisPage disPage, List<Order> orders) {

        Criteria criteria = getSession().createCriteria(entityClass);
        if (list != null) {  //如果条件集合不无为空,添加查询条件
            for (Criterion criterion : list) {
                criteria.add(criterion);
            }
        }

        if (orders != null) {//如果排序集合不为空,添加对应的排序条件
            for (Order order : orders) {
                criteria.addOrder(order);
            }
        }

        if (disPage != null) {//如果分页对象不会空,添加总记录数,并设置查询的开始记录数与最大返回记录数
            criteria.setProjection(Projections.count(getIdPropertyName(entityClass)));
            Object obj = criteria.uniqueResult();
            disPage.setTotalRow(obj != null ? Long.parseLong(obj.toString()) : 0);
            criteria.setProjection(null);

            criteria.setFirstResult(disPage.getStartNum());
            criteria.setMaxResults(disPage.getPageSize());
        }
        criteria.setCacheable(this.isCacheable);
        return criteria.list();
    }

    /**
     * 无条件查询对象所有记录。
     *
     * @param entityClass 实体类型
     * @param disPage     分页对象,当参数值为 null 时,所查询的对象列表集为数据库中所有记录。当参数不会空时,按对象所指定的开始记录号与每页记录数查询对象列表集。开始记录号默认值为0,每页记录数默认值为20.
     * @param orders      排序条件集合,当参数值为 null 时,不对查询对象的列表集进行排序。
     * @return 记录列表
     */
    public List findAll(Class entityClass, DisPage disPage, List<Order> orders) {
        return findList(entityClass, null, disPage, orders);
    }

    /**
     * 查询唯一记录,如果查询出多记一样的记录时,只返回第一条记录。否则为空
     *
     * @param entityClass 实体对象类型
     * @param list        查询条件集合,当参数值为 null 时,查询对象的所有记录数。当参数不会空时,按条件查询指定的对象列表集。
     * @return 唯一记录
     */
    public Object findUniqueResult(Class entityClass, List<Criterion> list) {
        Criteria criteria = getSession().createCriteria(entityClass);
        if (list != null) {
            for (Criterion criterion : list) {
                criteria.add(criterion);
            }
        }
        criteria.setCacheable(this.isCacheable);
        return criteria.uniqueResult();
    }

    /**
     * 查询唯一记录,如果查询出多记一样的记录时,只返回第一条记录。否则为空
     *
     * @param entity 查询的实体对象
     * @return 唯一记录
     */
    public Object findUniqueResult(IEntity entity) {
        Class entityClass = entity.getClass();
        Serializable serializable = getIdPropertyValue(entityClass, entity);

        String[] propertyNames = getClassMetadata(entityClass).getPropertyNames();
        Object[] proertyValues = getClassMetadata(entityClass).getPropertyValues(entity, EntityMode.POJO);

        Criteria criteria = getSession().createCriteria(entityClass);
        if(serializable != null){
            criteria.add(Restrictions.eq(getIdPropertyName(entityClass),serializable));
        }

        for (int i = 0; i < proertyValues.length; i++) {
            if (proertyValues[i] != null) {
                criteria.add(Restrictions.eq(propertyNames[i], proertyValues[i]));
            }
        }
              criteria.setCacheable(this.isCacheable);
        return criteria.uniqueResult();
    }

    /**
     * 根据主键查询实体,JPA的 find 查询接口实现。
     *
     * @param entityClass 实体类型
     * @param primaryKey  实体主键
     * @return 返回实体内容
     * @throws Exception 在查询的对象为空并重新new一个对象实例时的异常
     */
    public IEntity findByPk(Class<?> entityClass, Object primaryKey) throws Exception {
        Object o = getEntityManager().find(entityClass, primaryKey);
        if (o == null) {
            return (IEntity) entityClass.newInstance();
        }
        return (IEntity) o;
    }

    /**
     * 保存实体,JPA的 persist 保存接口实现。
     *
     * @param entity 要保存的实体
     */
    public void persist(IEntity entity) {
        getEntityManager().persist(entity);
    }

    /**
     * 保存实体,JPA的 merge 保存接口实现。
     *
     * @param entity 要保存的实体
     */
    public void merge(IEntity entity) {
        getEntityManager().merge(entity);
    }

    /**
     * 简单实体更新 , Hibernate的 update 接口实现。
     *
     * @param entity 实体对象
     * @throws Exception 异常
     */
    public void updateEntity(IEntity entity) throws Exception {
        Class entityClass = entity.getClass();
        Serializable serializable = getIdPropertyValue(entityClass, entity);

        String[] propertyNames = getClassMetadata(entityClass).getPropertyNames();
        Object[] proertyValues = getClassMetadata(entityClass).getPropertyValues(entity, EntityMode.POJO);


        Object obj = getSession().get(entityClass, serializable, LockMode.UPGRADE);

        Method setM = null;
        for (int i = 0; i < proertyValues.length; i++) {
            if (proertyValues[i] != null) {
                setM = obj.getClass().getMethod("set" + propertyNames[i], new Class[]{proertyValues[i].getClass()});
                setM.invoke(obj, proertyValues[i]);
            }
        }

        getSession().update(obj);
    }

    /**
     * 删除实体
     *
     * @param entityClass 实体类型
     * @param primaryKey  实体主键
     * @throws Exception 查询接口所抛出的异常。
     */
    public void delete(Class entityClass, Object primaryKey) throws Exception {
        String name = getIdPropertyName(entityClass);
        StringBuffer sql = new StringBuffer();
        sql.append("delete ");
        sql.append(classMetadata.getEntityName());
        sql.append("  where ");
        sql.append(name);
        sql.append("  =:");
        sql.append(name);

        Query query = getSession().createQuery(sql.toString());
        query.setParameter(name, primaryKey);
        query.executeUpdate();
    }


    /**
     * 批量删除实体
     *
     * @param entityClass    实体类型
     * @param primaryKeyList 主键对象列表集
     */
    public void deleteList(Class entityClass, List primaryKeyList) {

        String name = getIdPropertyName(entityClass);
        StringBuffer sql = new StringBuffer();
        sql.append("delete ");
        sql.append(classMetadata.getEntityName());
        sql.append("  where ");
        sql.append(name);
        sql.append("  in (  :");
        sql.append(name);
        sql.append(" ) ");

        Query query = getSession().createQuery(sql.toString());
        query.setParameterList(name, primaryKeyList);
        query.executeUpdate();
    }


    public void setEntityManagerHelper(IEntityManagerHelper entityManagerHelper) {
        this.entityManagerHelper = entityManagerHelper;
    }

    public Session getSession() {

        if (this.session == null || !this.session.isOpen())
            this.session = this.entityManagerHelper.getSession();

        return this.session;
    }

    public void setCacheable(boolean cacheable) {
        isCacheable = cacheable;
    }

    private EntityManager getEntityManager() {
        if (this.entityManager == null)
            this.entityManager = this.entityManagerHelper.getEntityManager();
        return this.entityManager;
    }

    private ClassMetadata getClassMetadata(Class entityClass) {
        if (this.classMetadata == null) {
            this.classMetadata = getSession().getSessionFactory().getClassMetadata(entityClass);
        }
        return this.classMetadata;
    }

    /**
     * 根据实体对象的类型,获取实体对象的主键属性名称
     *
     * @param entityClass 实体类型
     * @return 对象的主键属性名称
     */
    private String getIdPropertyName(Class entityClass) {
        return getClassMetadata(entityClass).getIdentifierPropertyName();
    }

    /**
     * 根据实体对象的类型,获取实体对象主键值
     *
     * @param entityClass 实体类型
     * @param entity      实体对象
     * @return 对象的主键值
     */
    private Serializable getIdPropertyValue(Class entityClass, IEntity entity) {
        return getClassMetadata(entityClass).getIdentifier(entity, EntityMode.POJO);
    }
}
